// -----------------------------
// Author: 		Dhaenens Gert	
// Creation: 	10/27/2014 6:44:58 PM				
// -----------------------------

#include "SkyboxPass.h"

#include "Application.h"
#include "FileManager.h"
#include "RenderManager.h"

#include "Scene.h"
#include "Camera.h"

struct SkyboxVertex
{
	Vector4	P;
};

struct MatrixBuffer
{
	Matrix	WorldViewProj;
};

SkyboxPass::SkyboxPass(RenderManager* lpRenderManager, const RenderTargets& targets)
	: RenderPass(lpRenderManager, "Skybox", targets)
	, m_lpVertexShader(nullptr)
	, m_lpPixelShader(nullptr)
	, m_lpInputLayout(nullptr)
	, m_lpVertexBuffer(nullptr)
	, m_lpIndexBuffer(nullptr)
	, m_lpMatrixBuffer(nullptr)
	, m_lpTexture(nullptr)
	, m_lpSampler(nullptr)
{
}

SkyboxPass::~SkyboxPass()
{
	NullRelease(&m_lpSampler);
	NullRelease(&m_lpMatrixBuffer);
	NullRelease(&m_lpIndexBuffer);
	NullRelease(&m_lpVertexBuffer);
	NullRelease(&m_lpInputLayout);
	NullRelease(&m_lpPixelShader);
	NullRelease(&m_lpVertexShader);
}

void SkyboxPass::Init(Application* lpApp)
{
	// Load the Shaders
	m_lpVertexShader = ShaderCompiler::LoadVertexFromFile(m_lpRenderManager->GetDevice(), lpApp->GetFiles()->GetFileName(_T("Shader_SkyboxVertex")).c_str());
	m_lpPixelShader = ShaderCompiler::LoadPixelFromFile(m_lpRenderManager->GetDevice(), lpApp->GetFiles()->GetFileName(_T("Shader_SkyboxPixel")).c_str());
	m_lpInputLayout = IInputLayout::Create(m_lpRenderManager->GetDevice(), m_lpVertexShader);

	// Create the Skybox Cube
	// Create the Cube
	vector<SkyboxVertex> vertices;
	vector<UInt32> indices;
	auto addVertex = [&vertices](const Vector3& p)
	{
		SkyboxVertex v;
		v.P = Vector4(p, 1.0f);
		vertices.push_back(v);
	};
	auto addFace = [&indices](UInt32 a, UInt32 b, UInt32 c, UInt32 d)
	{
		indices.push_back(a);
		indices.push_back(b);
		indices.push_back(c);

		indices.push_back(a);
		indices.push_back(c);
		indices.push_back(d);
	};
	Float d = 0.5f;

	// Create the Vertices
	addVertex(Vector3(-d, -d, -d)); // Left Bottom Front 0
	addVertex(Vector3(-d, d, -d)); // Left Top Front 1
	addVertex(Vector3(d, d, -d)); // Right Top Front 2
	addVertex(Vector3(d, -d, -d)); // Right Bottom Front 3

	addVertex(Vector3(-d, -d, d)); // Left Bottom Back 4
	addVertex(Vector3(-d, d, d)); // Left Top Back 5
	addVertex(Vector3(d, d, d)); // Right Top Back 6
	addVertex(Vector3(d, -d, d)); // Right Bottom Back 7

	// Create the Indices
	addFace(0, 1, 2, 3); // Front Face
	addFace(4, 5, 6, 7); // Back Face
	addFace(1, 5, 6, 2); // Top Face
	addFace(0, 4, 7, 3); // Bottom Face
	addFace(4, 5, 1, 0); // Left Face
	addFace(3, 2, 6, 7); // Right Face

	// Create the Buffers
	m_lpVertexBuffer = IBuffer::Create(m_lpRenderManager->GetDevice(), BufferType::Vertex, Usage::Default, 0, sizeof(SkyboxVertex), (UInt32) vertices.size(), vertices.data());
	m_lpIndexBuffer = IBuffer::Create(m_lpRenderManager->GetDevice(), BufferType::Index, Usage::Default, 0, sizeof(UInt32), (UInt32) indices.size(), indices.data());
	m_lpMatrixBuffer = IBuffer::Create(m_lpRenderManager->GetDevice(), BufferType::Constant, Usage::Default, 0, sizeof(MatrixBuffer), 1, nullptr);

	// Create the Sampler
	m_lpSampler = ISamplerState::Create(m_lpRenderManager->GetDevice(), AddressMode::Wrap, AddressMode::Wrap, AddressMode::Wrap);
}
void SkyboxPass::Apply(Application* lpApp, Scene* lpScene)
{
	IRenderDevice* lpDevice = m_lpRenderManager->GetDevice();

	Camera* lpCamera = lpScene->GetCamera();
	Matrix world = Matrix::Translation(lpCamera->GetPosition());
	MatrixBuffer matBuffer = {};
	matBuffer.WorldViewProj = world * lpCamera->GetView() * lpCamera->GetProjection();
	m_lpMatrixBuffer->Update(&matBuffer, sizeof(MatrixBuffer));

	lpDevice->SetRenderTarget(m_Targets.AmbientTarget, nullptr);

	lpDevice->SetVertexShader(m_lpVertexShader);
	lpDevice->SetHullShader(nullptr);
	lpDevice->SetDomainShader(nullptr);
	lpDevice->SetGeometryShader(nullptr);
	lpDevice->SetPixelShader(m_lpPixelShader);
	lpDevice->SetInputLayout(m_lpInputLayout);
	lpDevice->SetTopology(Topology::TriangleList);

	m_lpVertexShader->SetBufferAsConstant(0, m_lpMatrixBuffer);

	m_lpPixelShader->SetSamplerState(0, m_lpSampler);
	if (m_lpTexture)
		m_lpPixelShader->SetTextureAsResource(0, m_lpTexture.Get());
	else
		Log::WriteLine(_T("[WARNING]\tSkybox Pass is enabled. But no texture is set."));

	lpDevice->SetVertexBuffer(0, m_lpVertexBuffer);
	lpDevice->SetIndexBuffer(m_lpIndexBuffer);

	RenderState renderState = m_lpRenderManager->GetRenderState();
	if (renderState != RenderState::Wireframe)
		m_lpRenderManager->SetRenderState(RenderState::DefaultNoCull, false);
	lpDevice->DrawIndexed(m_lpIndexBuffer->GetCount());
	if (renderState != RenderState::Wireframe)
		m_lpRenderManager->SetRenderState(renderState, false);
}

void SkyboxPass::SetTexture(RefPtr<ITexture2D> lpTexture)
{
	m_lpTexture = lpTexture;
}